"""笔记分享模块的测试用例

测试笔记分享功能，包括分享创建、访问、评论等。
"""

import json
from django.test import TestCase, TransactionTestCase
from django.urls import reverse
from django.contrib.auth import get_user_model
from django.db import transaction
from django.utils import timezone
from datetime import timedelta
from rest_framework.test import APITestCase, APIClient
from rest_framework import status
from rest_framework_simplejwt.tokens import RefreshToken

from .models import SharedNote, ShareComment, ShareAccess, CommentLike, SharePermission
from .serializers import (
    SharedNoteSerializer, ShareCommentSerializer,
    ShareAccessSerializer, CommentLikeSerializer
)
from apps.notes.models import Note, Category

User = get_user_model()


class SharedNoteModelTest(TransactionTestCase):
    """分享笔记模型测试"""
    
    def setUp(self):
        """设置测试数据"""
        self.user = User.objects.create_user(
            username='testuser',
            email='test@example.com',
            password='testpass123'
        )
        
        self.note = Note.objects.create(
            title='测试笔记',
            content='这是一篇测试笔记内容',
            author=self.user
        )
    
    def test_create_shared_note(self):
        """测试创建分享笔记"""
        with transaction.atomic():
            shared_note = SharedNote.objects.create(
                note=self.note,
                shared_by=self.user,
                scope='public'
            )
        
        self.assertEqual(shared_note.note, self.note)
        self.assertEqual(shared_note.shared_by, self.user)
        self.assertEqual(shared_note.scope, 'public')
        self.assertTrue(shared_note.is_active)
        self.assertIsNotNone(shared_note.share_token)
    
    def test_share_token_generation(self):
        """测试分享token生成"""
        with transaction.atomic():
            shared_note = SharedNote.objects.create(
                note=self.note,
                shared_by=self.user
            )
        
        self.assertTrue(len(shared_note.share_token) > 0)
        self.assertTrue(shared_note.share_token.isalnum())
    
    def test_share_url_generation(self):
        """测试分享链接生成"""
        with transaction.atomic():
            shared_note = SharedNote.objects.create(
                note=self.note,
                shared_by=self.user
            )
        
        share_url = shared_note.get_share_url()
        self.assertIn(shared_note.share_token, share_url)
    
    def test_is_expired_property(self):
        """测试过期检查"""
        # 创建已过期的分享
        past_time = timezone.now() - timedelta(hours=1)
        
        with transaction.atomic():
            expired_share = SharedNote.objects.create(
                note=self.note,
                shared_by=self.user,
                expires_at=past_time
            )
        
        self.assertTrue(expired_share.is_expired)
        
        # 创建未过期的分享
        future_time = timezone.now() + timedelta(hours=1)
        
        with transaction.atomic():
            active_share = SharedNote.objects.create(
                note=self.note,
                shared_by=self.user,
                expires_at=future_time
            )
        
        self.assertFalse(active_share.is_expired)
    
    def test_is_view_limit_reached(self):
        """测试访问次数限制"""
        with transaction.atomic():
            shared_note = SharedNote.objects.create(
                note=self.note,
                shared_by=self.user,
                max_views=5,
                view_count=5
            )
        
        self.assertTrue(shared_note.is_view_limit_reached)
        
        shared_note.view_count = 3
        shared_note.save()
        
        self.assertFalse(shared_note.is_view_limit_reached)
    
    def test_is_accessible_property(self):
        """测试可访问性检查"""
        with transaction.atomic():
            accessible_share = SharedNote.objects.create(
                note=self.note,
                shared_by=self.user,
                is_active=True
            )
        
        self.assertTrue(accessible_share.is_accessible)
        
        # 测试非活跃状态
        accessible_share.is_active = False
        accessible_share.save()
        
        self.assertFalse(accessible_share.is_accessible)


class ShareCommentModelTest(TransactionTestCase):
    """分享评论模型测试"""
    
    def setUp(self):
        """设置测试数据"""
        self.user = User.objects.create_user(
            username='testuser',
            email='test@example.com',
            password='testpass123'
        )
        
        self.note = Note.objects.create(
            title='测试笔记',
            content='测试内容',
            author=self.user
        )
        
        self.shared_note = SharedNote.objects.create(
            note=self.note,
            shared_by=self.user
        )
    
    def test_create_comment(self):
        """测试创建评论"""
        with transaction.atomic():
            comment = ShareComment.objects.create(
                shared_note=self.shared_note,
                author=self.user,
                content='这是一条测试评论'
            )
        
        self.assertEqual(comment.shared_note, self.shared_note)
        self.assertEqual(comment.author, self.user)
        self.assertEqual(comment.content, '这是一条测试评论')
        self.assertTrue(comment.is_approved)
        self.assertFalse(comment.is_deleted)
    
    def test_create_reply_comment(self):
        """测试创建回复评论"""
        with transaction.atomic():
            parent_comment = ShareComment.objects.create(
                shared_note=self.shared_note,
                author=self.user,
                content='父评论'
            )
            
            reply_comment = ShareComment.objects.create(
                shared_note=self.shared_note,
                author=self.user,
                content='回复评论',
                parent=parent_comment
            )
        
        self.assertEqual(reply_comment.parent, parent_comment)
        self.assertIn(reply_comment, parent_comment.replies.all())


class SharedNoteAPITest(APITestCase):
    """分享笔记API测试"""
    
    def setUp(self):
        """设置测试数据"""
        self.client = APIClient()
        self.user = User.objects.create_user(
            username='testuser',
            email='test@example.com',
            password='testpass123'
        )
        
        # 设置认证
        refresh = RefreshToken.for_user(self.user)
        self.access_token = str(refresh.access_token)
        self.client.credentials(HTTP_AUTHORIZATION=f'Bearer {self.access_token}')
        
        # 创建测试笔记
        self.note = Note.objects.create(
            title='测试笔记',
            content='测试内容',
            author=self.user
        )
    
    def test_create_share(self):
        """测试创建分享"""
        share_data = {
            'note': self.note.id,
            'scope': 'public'
        }
        
        response = self.client.post(
            reverse('sharing:share-list'),
            share_data,
            format='json'
        )
        
        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
        self.assertEqual(response.data['scope'], 'public')
        self.assertTrue(SharedNote.objects.filter(note=self.note).exists())
    
    def test_list_shares(self):
        """测试获取分享列表"""
        # 创建测试分享
        SharedNote.objects.create(
            note=self.note,
            shared_by=self.user,
            scope='public'
        )
        
        response = self.client.get(reverse('sharing:share-list'))
        
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(len(response.data['results']), 1)
    
    def test_update_share(self):
        """测试更新分享"""
        shared_note = SharedNote.objects.create(
            note=self.note,
            shared_by=self.user,
            scope='private'
        )
        
        update_data = {
            'scope': 'public'
        }
        
        response = self.client.patch(
            reverse('sharing:share-detail', kwargs={'pk': shared_note.id}),
            update_data,
            format='json'
        )
        
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(response.data['scope'], 'public')
        
        # 验证数据库中的数据已更新
        shared_note.refresh_from_db()
        self.assertEqual(shared_note.scope, 'public')
    
    def test_delete_share(self):
        """测试删除分享"""
        shared_note = SharedNote.objects.create(
            note=self.note,
            shared_by=self.user
        )
        
        response = self.client.delete(
            reverse('sharing:share-detail', kwargs={'pk': shared_note.id})
        )
        
        self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
        self.assertFalse(SharedNote.objects.filter(id=shared_note.id).exists())
    
    def test_access_shared_note_by_token(self):
        """测试通过token访问分享笔记"""
        shared_note = SharedNote.objects.create(
            note=self.note,
            shared_by=self.user,
            scope='public'
        )
        
        # 移除认证，模拟匿名访问
        self.client.credentials()
        
        response = self.client.get(
            reverse('sharing:share-access', kwargs={'pk': shared_note.share_token})
        )
        
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(response.data['note_detail']['title'], '测试笔记')
        
        # 验证访问记录已创建
        self.assertTrue(
            ShareAccess.objects.filter(shared_note=shared_note).exists()
        )
    
    def test_access_expired_share(self):
        """测试访问已过期的分享"""
        past_time = timezone.now() - timedelta(hours=1)
        
        shared_note = SharedNote.objects.create(
            note=self.note,
            shared_by=self.user,
            expires_at=past_time
        )
        
        # 移除认证
        self.client.credentials()
        
        response = self.client.get(
            reverse('sharing:share-access', kwargs={'pk': shared_note.share_token})
        )
        
        self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
    
    def test_access_nonexistent_share(self):
        """测试访问不存在的分享"""
        # 移除认证
        self.client.credentials()
        
        response = self.client.get(
            reverse('sharing:share-access', kwargs={'pk': 'nonexistent_token'})
        )
        
        self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
    
    def test_get_share_statistics(self):
        """测试获取分享统计"""
        shared_note = SharedNote.objects.create(
            note=self.note,
            shared_by=self.user,
            view_count=10
        )
        
        response = self.client.get(
            reverse('sharing:share-statistics', kwargs={'pk': shared_note.id})
        )
        
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(response.data['total_access'], 10)
        self.assertIn('unique_visitors', response.data)
        self.assertIn('comments_count', response.data)
    
    def test_unauthorized_access_to_share_management(self):
        """测试未授权访问分享管理"""
        # 移除认证
        self.client.credentials()
        
        response = self.client.get(reverse('sharing:share-list'))
        
        self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)
    
    def test_access_other_user_share_management(self):
        """测试访问其他用户的分享管理"""
        # 创建另一个用户
        other_user = User.objects.create_user(
            username='otheruser',
            email='other@example.com',
            password='otherpass123'
        )
        
        other_note = Note.objects.create(
            title='其他用户笔记',
            content='内容',
            author=other_user
        )
        
        other_share = SharedNote.objects.create(
            note=other_note,
            shared_by=other_user
        )
        
        # 尝试更新其他用户的分享
        response = self.client.patch(
            reverse('sharing:share-detail', kwargs={'pk': other_share.id}),
            {'scope': 'public'},
            format='json'
        )
        
        self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)


class ShareCommentAPITest(APITestCase):
    """分享评论API测试"""
    
    def setUp(self):
        """设置测试数据"""
        self.client = APIClient()
        self.user = User.objects.create_user(
            username='testuser',
            email='test@example.com',
            password='testpass123'
        )
        
        # 设置认证
        refresh = RefreshToken.for_user(self.user)
        self.access_token = str(refresh.access_token)
        self.client.credentials(HTTP_AUTHORIZATION=f'Bearer {self.access_token}')
        
        # 创建测试数据
        self.note = Note.objects.create(
            title='测试笔记',
            content='测试内容',
            author=self.user
        )
        
        self.shared_note = SharedNote.objects.create(
            note=self.note,
            shared_by=self.user
        )
    
    def test_create_comment(self):
        """测试创建评论"""
        comment_data = {
            'shared_note': self.shared_note.id,
            'content': '这是一条测试评论'
        }
        
        response = self.client.post(
            reverse('sharing:comment-list'),
            comment_data,
            format='json'
        )
        
        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
        self.assertEqual(response.data['content'], '这是一条测试评论')
        self.assertTrue(
            ShareComment.objects.filter(
                shared_note=self.shared_note,
                content='这是一条测试评论'
            ).exists()
        )
    
    def test_list_comments(self):
        """测试获取评论列表"""
        # 创建测试评论
        ShareComment.objects.create(
            shared_note=self.shared_note,
            author=self.user,
            content='测试评论1'
        )
        
        ShareComment.objects.create(
            shared_note=self.shared_note,
            author=self.user,
            content='测试评论2'
        )
        
        response = self.client.get(
            reverse('sharing:comment-list'),
            {'shared_note': self.shared_note.id}
        )
        
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(len(response.data['results']), 2)
    
    def test_like_comment(self):
        """测试点赞评论"""
        comment = ShareComment.objects.create(
            shared_note=self.shared_note,
            author=self.user,
            content='测试评论'
        )
        
        response = self.client.post(
            reverse('sharing:comment-like', kwargs={'pk': comment.id})
        )
        
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertTrue(response.data['liked'])
        
        # 验证点赞记录已创建
        self.assertTrue(
            CommentLike.objects.filter(
                comment=comment,
                user=self.user
            ).exists()
        )
        
        # 再次点赞应该取消点赞
        response = self.client.post(
            reverse('sharing:comment-like', kwargs={'pk': comment.id})
        )
        
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertFalse(response.data['liked'])
    
    def test_report_comment(self):
        """测试举报评论"""
        comment = ShareComment.objects.create(
            shared_note=self.shared_note,
            author=self.user,
            content='测试评论'
        )
        
        report_data = {
            'reason': '不当内容'
        }
        
        response = self.client.post(
            reverse('sharing:comment-report', kwargs={'pk': comment.id}),
            report_data,
            format='json'
        )
        
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertIn('举报已提交', response.data['message'])


class ShareAccessTest(TransactionTestCase):
    """分享访问记录测试"""
    
    def setUp(self):
        """设置测试数据"""
        self.user = User.objects.create_user(
            username='testuser',
            email='test@example.com',
            password='testpass123'
        )
        
        self.note = Note.objects.create(
            title='测试笔记',
            content='测试内容',
            author=self.user
        )
        
        self.shared_note = SharedNote.objects.create(
            note=self.note,
            shared_by=self.user
        )
    
    def test_create_access_record(self):
        """测试创建访问记录"""
        with transaction.atomic():
            access_record = ShareAccess.objects.create(
                shared_note=self.shared_note,
                visitor=self.user,
                ip_address='127.0.0.1',
                user_agent='Test Browser'
            )
        
        self.assertEqual(access_record.shared_note, self.shared_note)
        self.assertEqual(access_record.visitor, self.user)
        self.assertEqual(access_record.ip_address, '127.0.0.1')
        self.assertEqual(access_record.user_agent, 'Test Browser')
    
    def test_anonymous_access_record(self):
        """测试匿名访问记录"""
        with transaction.atomic():
            access_record = ShareAccess.objects.create(
                shared_note=self.shared_note,
                visitor=None,  # 匿名访问
                ip_address='192.168.1.1',
                user_agent='Anonymous Browser'
            )
        
        self.assertEqual(access_record.shared_note, self.shared_note)
        self.assertIsNone(access_record.visitor)
        self.assertEqual(access_record.ip_address, '192.168.1.1')
